package com.dyt.itool.parser;

import com.dyt.itool.generator.*;
import com.dyt.itool.model.HttpCommand;
import com.dyt.itool.model.IProperty;
import com.dyt.itool.model.Struct;
import com.dyt.itool.model.TcpCommand;
import com.dyt.itool.model.impl.BasicProperty;
import com.dyt.itool.model.impl.ListProperty;
import com.dyt.itool.model.impl.MapProperty;
import com.dyt.itool.model.impl.StructProperty;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class XmlCommandParser extends AbstractParser {
    private static Logger logger = Logger.getLogger(XmlCommandParser.class);
    @SuppressWarnings("unused")
    private static final String BASIC_TYPES = "basic_types";
    private static final String STRUCTS = "structs";
    private static final String TCP_COMMANDS = "tcp_commands";
    private static final String HTTP_COMMANDS = "http_commands";
    private static final String MODULE = "module";
    private static final String COMMAND = "command";


    private List<Struct> structList = new ArrayList<Struct>();
    private List<TcpCommand> tcpCommandList = new ArrayList<TcpCommand>();
    private List<HttpCommand> httpCommandList = new ArrayList<HttpCommand>();
    private Map<String, String> moduleList = new HashMap<String, String>();

    public XmlCommandParser() {

    }


    public void parse(String filename) {
        File f = new File(filename);
        if (!f.exists() || !f.isFile()) {
            logger.debug("要解析的文件不存在");
            return;
        }

        SAXReader saxReader = new SAXReader();
        Document dom;
        try {
            dom = saxReader.read(f);
        } catch (DocumentException e) {
            e.printStackTrace();
            logger.debug(e.getMessage());
            return;
        }

        parseStructList(dom);
        parseTcpCommandList(dom);
        parseHttpCommandList(dom);

    }


    public void generateCommandCode(Map<String, String> templateOutputMap) {


        for (String key : templateOutputMap.keySet()) {

            String value = templateOutputMap.get(key);

            try {

                generateCommandCode(key, value, getStructList(), getTcpCommandList(), getHttpCommandList());

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public void generateCommandCodeOneByOne(Map<String, String> templateOutputMap) {

        for (String templateFile : templateOutputMap.keySet()) {

            String outputFile = templateOutputMap.get(templateFile);

            try {

                Map<String, Object> dataMap = new HashMap<String, Object>();
                dataMap.put("cutil", new CHelper());
                dataMap.put("javautil", new JavaHelper());
                dataMap.put("luautil", new LuaHelper());


                for (HttpCommand struct : this.httpCommandList) {

                    dataMap.put("command", struct);


                    String outFile = outputFile.replace("?", struct.getStructName() + "Request");
                    VelocityUtils.generateFromVelocity(templateFile.replace("?", "request"), outFile, dataMap);

                    outFile = outputFile.replace("?", struct.getStructName() + "Response");
                    VelocityUtils.generateFromVelocity(templateFile.replace("?", "response"), outFile, dataMap);


                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }


    public void generateModelCodeOneByOne(Map<String, String> templateOutputMap) {

        for (String templateFile : templateOutputMap.keySet()) {

            String outputFile = templateOutputMap.get(templateFile);

            try {

                Map<String, Object> dataMap = new HashMap<String, Object>();
                dataMap.put("cutil", new CHelper());
                dataMap.put("javautil", new JavaHelper());
                dataMap.put("luautil", new LuaHelper());
                dataMap.put("typescriptutil", new TypeScriptHelper());


                for (Struct struct : structList) {

                    dataMap.put("model", struct);
                    String outFile = outputFile.replace("?", struct.getStructName() + "");
                    VelocityUtils.generateFromVelocity(templateFile, outFile, dataMap);

                    Writer standOut = new OutputStreamWriter(System.out);
                    VelocityUtils.generateFromVelocity(templateFile, standOut, dataMap);
                    standOut.flush();

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }


    public List<Struct> getStructList() {
        return structList;
    }

    public List<TcpCommand> getTcpCommandList() {
        return tcpCommandList;
    }

    public List<HttpCommand> getHttpCommandList() {
        return httpCommandList;

    }


    @SuppressWarnings("unchecked")
    private void parseStructList(Document dom) {

        Element root = dom.getRootElement();

        Element structsRoot = root.element(STRUCTS);
        if (structsRoot != null) {
            List<Element> structElements = structsRoot.elements("struct");
            for (Element element : structElements) {

                String structName = element.attributeValue("name");

                Struct struct = null;

                for (Struct tmpStruct : structList) {
                    if (structName.equals(tmpStruct.getStructName())) {
                        struct = tmpStruct;
                        break;
                    }
                }
                if (struct == null) {
                    struct = new Struct(structName);
                    structList.add(struct);
                }
                if (struct.getPropertyList().size() == 0) {
                    parseStruct(struct, element);
                }
            }
        }
    }

    private void addModuleInfo(String moduleId, String moduleName) {

		/*
        Struct module = new Struct( "module");
		module.addProperty( new BasicProperty( "string", "moduleName", moduleName ) );
		module.addProperty( new BasicProperty( "string", "moduleId", moduleId ) );
		*/

        //Map<String,String> map = new HashMap<String,String>();
        moduleList.put(moduleName, moduleId);
        //moduleList.add( map );

    }

    @SuppressWarnings("unchecked")
    private void parseTcpCommandList(Document dom) {
        Element root = dom.getRootElement();

        Element structsRoot = root.element(TCP_COMMANDS);
        if (structsRoot != null) {
            List<Element> structElements = structsRoot.elements(MODULE);
            for (Element element : structElements) {
                String moduleName = element.attributeValue("name", "default");
                String moduleId = element.attributeValue("id", "0");

                addModuleInfo(moduleId, moduleName);

                List<Element> commandElements = element.elements(COMMAND);
                for (Element commandEle : commandElements) {
                    String id = commandEle.attributeValue("id");
                    String type = commandEle.attributeValue("type");
                    String name = commandEle.attributeValue("name");

                    TcpCommand command = new TcpCommand(moduleId, moduleName, id, name, type);

                    parseStruct(command, commandEle);

                    tcpCommandList.add(command);
                }

            }
        }
    }

    @SuppressWarnings("unchecked")
    private void parseHttpCommandList(Document dom) {
        Element root = dom.getRootElement();

        Element structsRoot = root.element(HTTP_COMMANDS);
        if (structsRoot != null) {
            List<Element> structElements = structsRoot.elements(MODULE);
            for (Element element : structElements) {
                String moduleName = element.attributeValue("name", "default");
                String moduleId = element.attributeValue("id", "0");

                addModuleInfo(moduleId, moduleName);

                List<Element> commandElements = element.elements(COMMAND);
                for (Element commandEle : commandElements) {
                    String id = commandEle.attributeValue("id");
                    String name = commandEle.attributeValue("name");

                    HttpCommand command = new HttpCommand(moduleId, moduleName, id, name);
                    Element requestElement = commandEle.element("request");
                    Element responseElement = commandEle.element("response");

                    parseStruct(command.getRequest(), requestElement);
                    parseStruct(command.getResponse(), responseElement);

                    httpCommandList.add(command);
                }

            }
        }
    }

    @SuppressWarnings("unchecked")
    private void parseStruct(Struct struct, Element element) {
        List<Element> elementList = element.elements();
        for (Element propElement : elementList) {
            IProperty property = parseProperty(struct.getStructName(), propElement);
            struct.addProperty(property);
        }
    }


    private IProperty parseProperty(String structName, Element element) {
        String typeName = element.getName();
        String varName = element.attributeValue("name");

        //是否是可选的
        String optinalStr = element.attributeValue("optional", "false");
        boolean optional = false;
        if ("true".equalsIgnoreCase(optinalStr.trim())) {
            optional = true;
        }

        if ("list".equalsIgnoreCase(typeName)) {

            String elementType = element.attributeValue("type");
            if (elementType == null) {
                elementType = structName + varName;
            }

            Struct struct = getOrCreateStruct(elementType, element);
            if (struct == null) {
                return new ListProperty(typeName, varName, new BasicProperty(elementType, elementType));
            } else {
                return new ListProperty(typeName, varName, new StructProperty(struct, elementType));
            }

        } else if ("map".equalsIgnoreCase(typeName)) {

            Element keyElement = element.element("key");
            String key = keyElement.attributeValue("type");

            Element valueElement = element.element("value");
            String value = valueElement.attributeValue("type");

            Struct keyStruct = getOrCreateStruct(key, keyElement);
            Struct valueStruct = getOrCreateStruct(value, valueElement);

            IProperty keyProperty = keyStruct == null ? new BasicProperty(key, key) : new StructProperty(keyStruct, key);

            IProperty valueProperty = valueStruct == null ? new BasicProperty(value, value) : new StructProperty(valueStruct, value);

            return new MapProperty(typeName, varName, keyProperty, valueProperty);

        } else if (basicTypeMap.containsKey(typeName.toLowerCase())) {

            return new BasicProperty(basicTypeMap.get(typeName.toLowerCase()), varName);

        } else {

            StructProperty structProperty = new StructProperty(getOrCreateStruct(typeName, element), varName);
            structProperty.setOptinal(optional);

            return structProperty;

        }

    }


    private Struct getOrCreateStruct(String structName, Element element) {

        if (basicTypeMap.containsKey(structName.toLowerCase())) {
            return null;
        }

        for (Struct struct : structList) {
            if (structName.equals(struct.getStructName())) {
                return struct;
            }
        }

        Struct struct = new Struct(structName);
        parseStruct(struct, element);
        structList.add(struct);
        return struct;
    }


    private void generateCommandCode(String templateFile, String outputFile, List<Struct> modelList, List<TcpCommand> tcpCommandList, List<HttpCommand> httpCommandList) throws IOException {

        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("modelList", modelList);
        dataMap.put("tcpCommandList", tcpCommandList);
        dataMap.put("httpCommandList", httpCommandList);
        dataMap.put("moduleList", this.moduleList);

        dataMap.put("javautil", new JavaHelper());
        dataMap.put("cutil", new CHelper());
        dataMap.put("luautil", new LuaHelper());
        dataMap.put("csharputil", new CSharpHelper2());
        dataMap.put("typescriptutil", new TypeScriptHelper());

        VelocityUtils.generateFromVelocity(templateFile, outputFile, dataMap);

        Writer standOut = new OutputStreamWriter(System.out);
        VelocityUtils.generateFromVelocity(templateFile, standOut, dataMap);
        standOut.flush();
    }


}
